package pl.ps.wi.monitoring.station;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import pl.ps.wi.monitoring.station.enums.AlertEvents;
/**
* @file Phone.java
* @class Phone
* @date 19.05.2010
* @version 1.0
*
* @brief Klasa obsługuje telefon i zdarzenia z nim związane. Wysyła odpowiednie smsy.
* Klasa posiada metody służące do informowania o zbyt niskiej/wysokiej temperaturze na czujnikach
* oraz o pomyślnym/niepomyślnym uwierzytelnieniu użytkownika
*/

public class Phone implements Runnable {
/**	
* Zmienna wątku
*/
	private Thread thread;
/**
* Zmienna definiująca wiadomość sms
*/
	private String message;
/**
* Zmienna definiująca numer odbiorcy sms'a
*/
	private long number;
/**
* Zmienna statyczna określająca czy jest odblokowane wysyłanie sms'a informującego o zbyt niskiej temperaturze
*/
	private static volatile boolean sendLowTemp = true;
/**
* Zmienna statyczna określająca czy jest odblokowane wysyłanie sms'a informującego o zbyt wysokiej temperaturze
*/
	private static volatile boolean sendHightTemp = true;
/**
* Zmienna statyczna będąca grupą wątków
*/
	private static ThreadGroup threadGrup = new ThreadGroup("Sending message");
/**	
* Zmienna służąca do synchronizacji przy wysyłaniu wiadomości (działa jak mutex)
*/
	private static final Object lock = new Object(); 

/**
* Pole logiczne kontrolujące czy wiadomość o zniszczeniu systemu została już wysłana.
*/
	private static boolean alreadySent = false;

/**
* Metoda ustawia zmienną sendLowTemp na false i usypia wątek na 10,5dnia (czyli nie pozwala na wysyłanie sms'ów o zbyt niskiej temperaturze w tym czasie)
* @throws java.lang.InterruptedException
*/
	
	private static Thread lowTempThread = new Thread(new Runnable() {
		public void run() {
			sendLowTemp = false;
			try {
				Thread.sleep(900000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			sendLowTemp = true;
		};
	});
	
/**
* Metoda ustawia zmienna sendHightTemp na false i usypia wątek na 10,5dnia (czyli nie pozwala na wysyłnie sms'ów o zbyt wysokiej temperaturze w tym czasie)
* @throws java.lang.InterruptedException
*/
	
	private static Thread highTempThread = new Thread(new Runnable() {
		@Override
		public void run() {
			sendHightTemp = false;
			try {
				Thread.sleep(900000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			sendHightTemp = true;
		};
	});
/**
* Metoda wysyła wiadomości sms o zbyt wysokiej temperaturze(jezeli sendHighTemp==true)  oraz startuje wątek highTempThread jeżeli jest on uśpiony
*/
	public static void sendHighTemperatureMessage() {
		if (sendHightTemp) {
			sendMessages(AlertEvents.HIGH_TEMPERATURE);
			if (!highTempThread.isAlive()) {
				highTempThread.start();
			}
		}
	}
/**
* Metoda wysyła wiadomości sms o zbyt niskiej temperaturze(jeżeli sendLowTemp==true) oraz startuje wątek lowTempThread jeżeli highTempThread jest uśpiony
*/	
	public static void sendLowTemperaturemessage() {
		if (sendLowTemp) {
			sendMessages(AlertEvents.LOW_TEMPERATURE);
			if (!highTempThread.isAlive()) {
				lowTempThread.start();
			}
		}
	}
/**
* Metoda wysyła wiadomości sms o pomyślnym uwierzytelnieniu i otrzymaniu dostępu
*/	
	public static void sendAccessGrantedMessage() {
		sendMessages(AlertEvents.ACCESS_GRANTED);
	}
/**
* Metoda wysyła wiadomości sms o pomyślnym uwierzytelnieniu i nieotrzymaniu dostępu
*/		
	public static void sendAccessDiedMessage() {
		sendMessages(AlertEvents.ACCESS_DIED);
	}
/**
* Metoda wysyła wiadomości sms o uszkodzeniu systemu
*/
	public static void sendAnnihilationMessage() {
		if (!alreadySent) {
			sendMessages(AlertEvents.ANNIHILATION);
			alreadySent = true;
		}
	}
/**
* Metoda wysyła wiadomości sms o zbyt dużej liczbie osób w pomieszczeniu
*/
	public static void sendAccessRulesViolationMessage() {
		sendMessages(AlertEvents.ACCESS_RULES_VIOLATION);
	}

/**
* Metoda wysyła wiadomości sms informując (na podstawie parametru 'event') o odpowiednim zdarzeniu
* @param event Typ wyliczeniowy określający czego dotyczy wysyłana wiadomość
*/	
	private static void sendMessages(AlertEvents event) {
		for (long number: event.getNumbers()) {
			new Phone(event.getMessage(), number);
		}
	}
/**
* Prywatny konstruktor parametryczny klasy Phone
* Ustawia numer oraz wiadomość do wysłania oraz rozpoczyna wątek 
* @param message Wiadomość do wysłania
* @param number Numer do którego zostanie wysłana wiadomość
*/	
	private Phone(String message, long number) {
		this.message = message;
		this.number = number;
		
		thread = new Thread(this);
		thread.start();
	}
/**
* Metoda run() wykonywana przez wątek.
* Wysyła wiadomość przechowaną w zmiennej message pod numer znajdujący sie w zmiennej number
*/	
	@Override
	public void run() {
		sendMessage(message, number);
	}
	
/**
* Główna metoda klasy.
* Metoda klasy slużąca do wysyłania określonej wiadomości pod zadany numer z użyciem gammu.
* Używa unixowego programu gammu do wysłania określonej wiadomości sms pod zadany numer
* Do wysłania wiadomości używa pierwszej konfiguracji znajdującej się w pliku .gammurc katalogu domowego roota (/root).
* Metoda jest synchronizowana za pomocą mutexu "lock" tak, aby niemożliwe było w tej samej chwili wysyłanie wielu wiadomości (co powoduje rzucenie wyjątku)
* @param message Wysyłana wiadomość
* @param recipentNumber Numer odbiorcy
* @throws java.lang.InterruptedException
* @throws java.io.IOException
*/
	private void sendMessage(String message, long recipentNumber) {
		synchronized(Phone.lock) {
			String s = null;
			String err = null;
			Process p = null;
			String[] cmd = {"/bin/sh", "-c", "echo " + message + " | gammu --sendsms TEXT +48" + recipentNumber };
			ProcessBuilder builder = new ProcessBuilder(cmd);
			builder.environment().put("HOME", "/root");

			try {
				p = builder.start();
			} catch (IOException e) {
				e.printStackTrace();
			}

			BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
			BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));

			try {
				p.waitFor();
			} catch (InterruptedException e) {

				e.printStackTrace();
			}

			try {

				while ((s = stdInput.readLine()) != null) {
					System.out.println(s);
				}

			} catch (IOException e) {
				e.printStackTrace();
			}

			try {

				while ((err = stdError.readLine()) != null) {
					System.out.println(err);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

}
